package com.test.aqs;

import org.apache.commons.lang.math.RandomUtils;

import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Create by lu.jiang
 * Date 2021/2/24 17:26
 * eg. ww(互斥) wr(互斥) rr(shared)
 * 可以借鉴Doug Lea的ReentrantReadWriteLock，
 * 使用2^16以内的数作为写锁获取次数，使用2^16次放的倍数作为读锁获取次数，
 * 妙啊妙啊
 */
public class ReadWriteLock {
    private AtomicInteger rc = new AtomicInteger(0);

    private AtomicInteger wc = new AtomicInteger(0);

    private AtomicInteger c = new AtomicInteger(0);

    /**
     * may blocking
     */
    public boolean acquireReadLock() {
        while (wc.get() != 0) {
            // do noting
        }
        rc.incrementAndGet();

        return true;
    }

    public synchronized boolean acquireWriteLock() {
        while (wc.get() != 0 || rc.get() != 0) {
        }
        wc.incrementAndGet();
        return true;
    }

    public boolean releaseReadLock() {
        rc.decrementAndGet();
        return true;
    }

    public boolean releaseWriteLock() {
        wc.decrementAndGet();
        return true;
    }

    public boolean tryAcquireReadLock(long mills) {
        return false;
    }

    public boolean tryReleaseReadLock(long mills) {
        return false;
    }

    private static int data = 0;

    public static void main(String[] args) {
        ThreadPoolExecutor executor =
                new ThreadPoolExecutor(5, 5, 1, TimeUnit.SECONDS, new LinkedBlockingDeque<>(100));
        ReadWriteLock lock = new ReadWriteLock();
        for (int i = 1; i < 20; i++) {
            executor.execute(ReadWriteLock::readData);
            executor.execute(() -> incData(lock));
        }
        executor.shutdown();
        try {
            executor.awaitTermination(100, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("wc:%d,rc:%d,c:%d%n", lock.wc.get(), lock.rc.get(), lock.c.get());
    }

    private static void incData(ReadWriteLock lock) {
        if (lock.acquireWriteLock()) {
            int oldV = data;
            sleep(RandomUtils.nextInt(100));
            int newV = oldV + 1;
            data = newV;
            System.out.println(Thread.currentThread().getName() + " inc data to: " + newV);

            lock.releaseWriteLock();
        }
    }

    private static void sleep(int mills) {
        try {
            Thread.sleep(mills);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private static void readData() {
        System.out.println(Thread.currentThread().getName() + " read data: " + data);
    }
}
